<?php

namespace app\common\model;

use app\model\CouponOrder;
use EasyWeChat\Factory;
use think\facade\Db;
use think\facade\Log;
use think\facade\Request;

class WxpayV2
{
    private $config = null;
    private $app = null;

    /**
     * $agentId 支付账号：0使用公司支付账号，>0 使用对应代理商支付账号
     * $type    支付类型：wechat JSAPI支付，wechat_mweb H5 支付，agentId == 0 的时候才生效
     * $product 产品类型：mobile 话费，movie 影票，coupon 权益卡
     */
    public function __construct($agentId = 0, $type = 'wechat', $product = 'mobile')
    {
        if (empty($agentId)) {
            // 返佣，使用公司的支付账号：公司的微信支付账号分 JSAPI 和 H5 不同的账号
            $info = Db::name('config')
                ->where('type', $type)
                ->column('value', 'field');
        } else {
            // 预充，使用客户的支付账号
            $info = Db::name('agent_config')
                ->where('agent_id', $agentId)
                ->find();
        }

        if (empty($info)) {
            throw new \think\Exception('微信支付尚未配置');
        }

        $rootPath = root_path();
        $this->config = [
            // 必要配置
            'app_id' => $info['app_id'],
            'mch_id' => $info['mch_id'],
            'key' => $info['mch_key'],

            'cert_path' => '', // XXX: 绝对路径！！！！
            'key_path' => '', // XXX: 绝对路径！！！！

            'notify_url' => Request::domain().'/api/notify_v2/wxpayNotify/agentId/'.$agentId.'/type/'.$type.'/product/'.$product,

            /*
             * 日志配置
             *
             * level: 日志级别, 可选为：
             *         debug/info/notice/warning/error/critical/alert/emergency
             * path：日志文件位置(绝对路径!!!)，要求可写权限
             */
            'log' => [
                'default' => 'dev', // 默认使用的 channel，生产环境可以改为下面的 prod
                'channels' => [
                    // 测试环境
                    'dev' => [
                        'driver' => 'daily',
                        'path' => $rootPath.'runtime/wxpay_v2/wxpay.log',
                        'level' => 'debug',
                    ],
                    // 生产环境
                    'prod' => [
                        'driver' => 'daily',
                        'path' => $rootPath.'runtime/wxpay_v2/wxpay.log',
                        'level' => 'info',
                    ],
                ],
            ],
        ];
        if (!empty($info['cert_path'])) {
            $certRoot = app()->getRootPath().'data/';
            $this->config['cert_path'] = $certRoot.$info['cert_path'];
            $this->config['key_path'] = $certRoot.$info['key_path'];
        }

        $this->app = Factory::payment($this->config);
    }

    /**
     * 扫码支付.
     *
     * @params  $order_id   订单号
     * @params  $amount     充值金额。单位：元
     * @params  $name       充值标题：充值xxxx元
     */
    public function scan($data)
    {
        $order = [
            'body' => $data['name'],
            'out_trade_no' => $data['order_id'],
            'total_fee' => $data['amount'],
            'trade_type' => 'NATIVE',
            'product_id' => $data['order_id'],
        ];

        try {
            $result = $this->app->order->unify($order);
        } catch (\Exception $e) {
            return fail($e->getMessage());
        }

        return success('操作成功', [
            'out_trade_no' => $data['order_id'],
            'qrcode' => $result['code_url'],
        ]);
    }

    /**
     * 统一下单接口.
     *
     * 'body' => '腾讯充值中心-QQ会员充值',
     * 'out_trade_no' => '20150806125346',
     * 'total_fee' => 88,
     * 'spbill_create_ip' => Request::ip(),
     * 'trade_type' => 'JSAPI', // 请对应换成你的支付方式对应的值类型
     * 'openid' => 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o',
     */
    public function create($params)
    {
        return $this->app->order->unify($params);
    }

    public function jssdk($prepayId)
    {
        return $this->app->jssdk->bridgeConfig($prepayId, false); // 返回数组
    }

    /**
     * 退款.
     *
     * @params
     *  $transactionId   微信订单号：$trade_no
     *  $refundNumber    商户退款单号: $sub_order_id
     *  $totalFee        订单金额: $real_pay_price
     *  $refundFee       退款金额
     *  $desc            退款描述
     */
    public function refund($params)
    {
        // 参数分别为：微信订单号、商户退款单号、订单金额、退款金额、其他参数
        //$app->refund->byTransactionId(string $transactionId, string $refundNumber, int $totalFee, int $refundFee, array $config = []);

        $result = $this->app->refund->byTransactionId($params['transactionId'], $params['refundNumber'], $params['totalFee'], $params['refundFee'], [
            // 可在此处传入其他参数，详细参数见微信支付文档
            'refund_desc' => $params['desc'],
        ]);

        return $result;
    }

    /**
     * 企业付款到微信零钱.
     */
    public function transferToBalance($params)
    {
        $result = $this->app->transfer->toBalance([
            'partner_trade_no' => $params['partner_trade_no'], // 商户订单号，需保持唯一性(只能是字母或者数字，不能包含有符号)
            'openid' => $params['openid'],
            'check_name' => 'NO_CHECK', // NO_CHECK：不校验真实姓名, FORCE_CHECK：强校验真实姓名
            // 're_user_name' => '', // 如果 check_name 设置为FORCE_CHECK，则必填用户真实姓名
            'amount' => $params['amount'], // 企业付款金额，单位为分
            'desc' => $params['desc'], // 企业付款操作说明信息。必填
        ]);

        return $result;
    }

    /**
     * 查询付款到零钱的订单.
     */
    public function transferQueryBalanceOrder($partnerTradeNo)
    {
        return $this->app->transfer->queryBalanceOrder($partnerTradeNo);
    }

    /**
     * 支付结果通知.
     */
    public function handlePaidNotify($product)
    {
        $response = $this->app->handlePaidNotify(function ($message, $fail) use ($product) {
            Log::write('收到微信异步回调 '.$product.' '.json_encode($message, JSON_UNESCAPED_UNICODE), 'notice');
            // 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
            switch ($product) {
                case 'mobile':// 话费充值
                    $payLogTable = 'pay_log';
                    break;
                case 'movie': // 购买影票
                    $payLogTable = 'movie_pay_log';
                    break;
                case 'coupon': // 权益卡
                    $payLogTable = 'coupon_pay_log';
                    break;
                default:
                    return $fail('支付类型有误：'.$product);
                    break;
            }
            $payLog = Db::name($payLogTable)
                ->where('order_id', $message['out_trade_no'])
                ->find();

            if (empty($payLog) || $payLog['status'] == 2) { // 如果订单不存在 或者 订单已经支付过了
                Log::write('1', 'notice');

                return true; // 告诉微信，我已经处理完了，订单没找到，别再通知我了
            }

            ///////////// <- 建议在这里调用微信的【订单查询】接口查一下该笔订单的情况，确认是已经支付 /////////////

            if ($message['return_code'] !== 'SUCCESS') { // return_code 表示通信状态，不代表支付状态
                Log::write('2', 'notice');

                return $fail('通信失败，请稍后再通知我');
            }

            if ($message['result_code'] !== 'SUCCESS') {
                Log::write('支付失败:'.$message['out_trade_no'], 'notice');
                return true;
            }

            // 用户支付成功
            $payLog['trade_no'] = $message['transaction_id'];
            $payLog['real_pay_price'] = $message['total_fee'];

            Db::startTrans();
            try {
                Log::write('6', 'notice');
                switch ($product) {
                    case 'mobile': // 话费充值
                        $model = new \app\api\model\Mobile();
                        $model->handleSuccessV2($payLog);
                        break;
                    case 'movie': // 购买影票
                        $model = new \app\api\model\Movie();
                        $model->handleSuccess($payLog);
                        break;
                    case 'coupon':  // 权益卡
                        $model = new CouponOrder();
                        $model->paySuccess($payLog);
                        break;
                    default:
                        return $fail('类型有误.');
                        break;
                }

                Db::commit();
            } catch (\Exception $e) {
                Log::write('7', 'notice');
                Log::write($e->getMessage().'---'.$e->getLine().'=----'.$e->getFile(), 'error');

                Db::rollback();

                return $fail('支付失败：'.$e->getMessage());
            }

            Log::write('8', 'notice');

            return true; // 返回处理完成
        });

        Log::write('9', 'notice');

        return $response->send(); // return $response;
    }

    /**
     * 订单查询.
     */
    public function queryByOutTradeNumber($outTradeNo)
    {
        return $this->app->order->queryByOutTradeNumber($outTradeNo);
    }
}
